package com.xyht.sca_s.student_manage_system.modules.meeting.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xyht.sca_s.student_manage_system.common.commonReturn.CommonCodeEnum;
import com.xyht.sca_s.student_manage_system.common.commonReturn.CommonResult;
import com.xyht.sca_s.student_manage_system.common.commonReturn.ResponseResult;
import com.xyht.sca_s.student_manage_system.common.util.CacheUtil;
import com.xyht.sca_s.student_manage_system.common.util.TimeUtil;
import com.xyht.sca_s.student_manage_system.modules.meeting.entity.SmsMeetingBuilding;
import com.xyht.sca_s.student_manage_system.modules.meeting.entity.SmsMeetingBuildingFloor;
import com.xyht.sca_s.student_manage_system.modules.meeting.entity.SmsMeetingRoomInfo;
import com.xyht.sca_s.student_manage_system.modules.meeting.entity.SmsMeetingRoomPredetermineApply;
import com.xyht.sca_s.student_manage_system.modules.meeting.entity.req.SmsMeetingRoomInfoReq;
import com.xyht.sca_s.student_manage_system.modules.meeting.entity.resp.MeetingBuildingFloorResp;
import com.xyht.sca_s.student_manage_system.modules.meeting.entity.resp.MeetingBuildingResp;
import com.xyht.sca_s.student_manage_system.modules.meeting.entity.resp.OrgMeetingBuildingResp;
import com.xyht.sca_s.student_manage_system.modules.meeting.entity.resp.SmsMeetingRoomInfoResp;
import com.xyht.sca_s.student_manage_system.modules.meeting.mapper.SmsMeetingBuildingFloorMapper;
import com.xyht.sca_s.student_manage_system.modules.meeting.mapper.SmsMeetingBuildingMapper;
import com.xyht.sca_s.student_manage_system.modules.meeting.mapper.SmsMeetingRoomInfoMapper;
import com.xyht.sca_s.student_manage_system.modules.meeting.mapper.SmsMeetingRoomPredetermineApplyMapper;
import com.xyht.sca_s.student_manage_system.modules.meeting.service.SmsMeetingRoomInfoService;
import com.xyht.sca_s.student_manage_system.modules.org.entity.SmsOrgStructure;
import com.xyht.sca_s.student_manage_system.modules.org.mapper.SmsOrgStructureMapper;
import com.xyht.sca_s.student_manage_system.modules.user.entity.SmsUser;
import com.xyht.sca_s.student_manage_system.modules.user.entity.resp.SmsSearchUserResp;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.xyht.sca_s.student_manage_system.common.util.ParamCheckUtil.isNullOrEmpty;
import static com.xyht.sca_s.student_manage_system.modules.meeting.constant.MeetingRoomAllowStatusConstant.MEETING_ALLOW_STATUS_PASS;
import static com.xyht.sca_s.student_manage_system.modules.scheduleManagement.constant.RepeatTypeConstant.*;

/**
 * <p>
 * 会议室信息表 服务实现类
 * </p>
 *
 * @author XYHT
 * @since 2023-08-03
 */
@Service
public class SmsMeetingRoomInfoServiceImpl extends ServiceImpl<SmsMeetingRoomInfoMapper, SmsMeetingRoomInfo> implements SmsMeetingRoomInfoService {
    @Resource
    private SmsMeetingRoomInfoMapper smsMeetingRoomInfoMapper;
    @Resource
    private SmsOrgStructureMapper smsOrgStructureMapper;
    @Resource
    private SmsMeetingBuildingMapper smsMeetingBuildingMapper;
    @Resource
    private SmsMeetingBuildingFloorMapper smsMeetingBuildingFloorMapper;
    @Resource
    private SmsMeetingRoomPredetermineApplyMapper smsMeetingRoomPredetermineApplyMapper;
    @Resource
    private CacheUtil cacheUtil;

    @Override
    public ResponseResult addMeetingRoom(SmsMeetingRoomInfoReq meetingRoomReq) {
        if (isNullOrEmpty(meetingRoomReq.getName()) || isNullOrEmpty(meetingRoomReq.getCapacity())
                || meetingRoomReq.getDeviceList().isEmpty() || isNullOrEmpty(meetingRoomReq.getOrgId())
                || isNullOrEmpty(meetingRoomReq.getBuildingId()) || isNullOrEmpty(meetingRoomReq.getFloorId()) || isNullOrEmpty(meetingRoomReq.getRoomNum())
                || isNullOrEmpty(meetingRoomReq.getIsUse()) || isNullOrEmpty(meetingRoomReq.getIsAllow())
                || isNullOrEmpty(meetingRoomReq.getIsStamp()) || isNullOrEmpty(meetingRoomReq.getIsSign()) || isNullOrEmpty(meetingRoomReq.getIsBookTime())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }

        if (meetingRoomReq.getIsBookTime()) {
            if (isNullOrEmpty(meetingRoomReq.getStartTimeLimit()) || isNullOrEmpty(meetingRoomReq.getEndTimeLimit())
                    || isNullOrEmpty(meetingRoomReq.getSingleTimeLimit()) || isNullOrEmpty(meetingRoomReq.getEarliestBookTime())) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
        }

        SmsMeetingRoomInfo meetingRoom = new SmsMeetingRoomInfo();
        BeanUtils.copyProperties(meetingRoomReq, meetingRoom, "id");
        meetingRoom.setDeviceList(JSON.toJSONString(meetingRoomReq.getDeviceList()));
        if (meetingRoomReq.getIsAllow()) {//需要审批
            meetingRoom.setAllowUserIdList(JSON.toJSONString(meetingRoomReq.getAllowUserIdList()));
        }
        meetingRoom.setStartTimeLimit(TimeUtil.TimeFormat(meetingRoomReq.getStartTimeLimit()));
        meetingRoom.setEndTimeLimit(TimeUtil.TimeFormat(meetingRoomReq.getEndTimeLimit()));
        smsMeetingRoomInfoMapper.insert(meetingRoom);

        return CommonResult.success();
    }

    @Override
    public ResponseResult deleteMeetingRoom(String id) {
        if (isNullOrEmpty(id)) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }

        SmsMeetingRoomInfo meetingRoom = smsMeetingRoomInfoMapper.selectById(id);
        if (isNullOrEmpty(meetingRoom)) {
            return CommonResult.failed(CommonCodeEnum.MEETING_ROOM_NOT_EXIST);
        }

        smsMeetingRoomInfoMapper.deleteById(meetingRoom);

        return CommonResult.success();
    }

    @Override
    public ResponseResult updateMeetingRoom(SmsMeetingRoomInfoReq meetingRoomReq) {
        if (isNullOrEmpty(meetingRoomReq.getId()) || isNullOrEmpty(meetingRoomReq.getName())
                || isNullOrEmpty(meetingRoomReq.getCapacity()) || meetingRoomReq.getDeviceList().isEmpty() || isNullOrEmpty(meetingRoomReq.getOrgId())
                || isNullOrEmpty(meetingRoomReq.getBuildingId()) || isNullOrEmpty(meetingRoomReq.getFloorId()) || isNullOrEmpty(meetingRoomReq.getRoomNum())
                || isNullOrEmpty(meetingRoomReq.getIsUse()) || isNullOrEmpty(meetingRoomReq.getIsAllow()) || meetingRoomReq.getAllowUserIdList().isEmpty()
                || isNullOrEmpty(meetingRoomReq.getIsStamp()) || isNullOrEmpty(meetingRoomReq.getIsSign()) || isNullOrEmpty(meetingRoomReq.getIsBookTime())) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }

        if (meetingRoomReq.getIsBookTime()) {
            if (isNullOrEmpty(meetingRoomReq.getStartTimeLimit()) || isNullOrEmpty(meetingRoomReq.getEndTimeLimit())
                    || isNullOrEmpty(meetingRoomReq.getSingleTimeLimit()) || isNullOrEmpty(meetingRoomReq.getEarliestBookTime())) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
        }

        SmsMeetingRoomInfo meetingRoomInfo = smsMeetingRoomInfoMapper.selectById(meetingRoomReq.getId());
        if (isNullOrEmpty(meetingRoomInfo)) {
            return CommonResult.failed(CommonCodeEnum.MEETING_ROOM_NOT_EXIST);
        }

        SmsMeetingRoomInfo meetingRoom = new SmsMeetingRoomInfo();
        meetingRoom.setId(meetingRoomReq.getId());
        BeanUtils.copyProperties(meetingRoomReq, meetingRoom);
        meetingRoom.setDeviceList(JSON.toJSONString(meetingRoomReq.getDeviceList()));
        meetingRoom.setAllowUserIdList(JSON.toJSONString(meetingRoomReq.getAllowUserIdList()));
        meetingRoom.setStartTimeLimit(TimeUtil.TimeFormat(meetingRoomReq.getStartTimeLimit()));
        meetingRoom.setEndTimeLimit(TimeUtil.TimeFormat(meetingRoomReq.getEndTimeLimit()));
        smsMeetingRoomInfoMapper.updateById(meetingRoom);

        return CommonResult.success();
    }

    @Override
    public ResponseResult listMeetingRoom(Integer pageNum, Integer pageSize, String orgId, String buildingId, String floorId, String searchStr) {
        //包装查询条件
        LambdaQueryWrapper<SmsMeetingRoomInfo> meetingRoomQueryWrapper = new LambdaQueryWrapper<>();
        if (!isNullOrEmpty(orgId)) {
            meetingRoomQueryWrapper.eq(SmsMeetingRoomInfo::getOrgId, orgId);
        }
        if (!isNullOrEmpty(buildingId)) {
            meetingRoomQueryWrapper.eq(SmsMeetingRoomInfo::getBuildingId, buildingId);
        }
        if (!isNullOrEmpty(floorId)) {
            meetingRoomQueryWrapper.eq(SmsMeetingRoomInfo::getFloorId, floorId);
        }
        if (!isNullOrEmpty(searchStr)) {
            meetingRoomQueryWrapper.like(SmsMeetingRoomInfo::getCapacity, searchStr)
                    .or().like(SmsMeetingRoomInfo::getName, searchStr)
                    .or().like(SmsMeetingRoomInfo::getRoomNum, searchStr)
                    .or().like(SmsMeetingRoomInfo::getRemark, searchStr);
        }

        Page<SmsMeetingRoomInfo> page = new Page<>(pageNum, pageSize);
        smsMeetingRoomInfoMapper.selectPage(page, meetingRoomQueryWrapper);

        List<SmsMeetingRoomInfoResp> meetingRoomInfoRespList = page.getRecords().stream().map(meetingRoom -> {
            SmsMeetingRoomInfoResp meetingRoomResp = new SmsMeetingRoomInfoResp();
            BeanUtils.copyProperties(meetingRoom, meetingRoomResp);
            meetingRoomResp.setDeviceList(JSON.parseArray(meetingRoom.getDeviceList(), String.class));
            meetingRoomResp.setStartTimeLimit(TimeUtil.TimeFormatSting(meetingRoom.getStartTimeLimit()));
            meetingRoomResp.setEndTimeLimit(TimeUtil.TimeFormatSting(meetingRoom.getEndTimeLimit()));
            List<String> allowUserIdList = JSON.parseArray(meetingRoom.getAllowUserIdList(), String.class);
            if (allowUserIdList != null && !allowUserIdList.isEmpty()) {
                meetingRoomResp.setAllowUserIdList(allowUserIdList);
                List<SmsSearchUserResp> userRespArrayList = new ArrayList<>();
                for (String allowUserId : allowUserIdList) {
                    //获取当前用户信息并进行设置
                    SmsUser smsUser = cacheUtil.getUserInfo(allowUserId);
                    SmsSearchUserResp userResp = new SmsSearchUserResp();
                    if (smsUser != null) {
                        BeanUtils.copyProperties(smsUser, userResp);
                    }
                    userRespArrayList.add(userResp);
                }
                meetingRoomResp.setAllowUserInfo(userRespArrayList);
            }

            SmsOrgStructure org = smsOrgStructureMapper.selectById(meetingRoom.getOrgId());
            meetingRoomResp.setOrgName(org.getOrgName());
            SmsMeetingBuilding building = smsMeetingBuildingMapper.selectById(meetingRoom.getBuildingId());
            meetingRoomResp.setBuildingName(building.getName());
            SmsMeetingBuildingFloor floor = smsMeetingBuildingFloorMapper.selectById(meetingRoom.getFloorId());
            meetingRoomResp.setFloorName(floor.getName());

            return meetingRoomResp;
        }).collect(Collectors.toList());

        return CommonResult.success(meetingRoomInfoRespList, (int) page.getTotal());
    }

    @Override
    public ResponseResult listUsableMeetingRoom(Date startDate, Date endDate,
                                                Integer repeatType, Date repeatEndTime, List<Integer> repeatCustomWeek,
                                                List<String> deviceList, Integer capacity, String searchStr) {
        //校验前端参数
        if (isNullOrEmpty(startDate, endDate, repeatType) && (repeatType < 0 || repeatType > 4)) {
            return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
        }
        //校验日期范围
        if (startDate.compareTo(endDate) > 0 || endDate.compareTo(TimeUtil.GetEndDate(startDate)) > 0 || startDate.compareTo(new Date()) < 0) {
            return CommonResult.failed(CommonCodeEnum.TIME_CONTROL_RANGE_ILLEGAL);
        }
        //关于重复相关校验
        if (repeatType == REPEAT_TYPE_EVERY_CUSTOM) {
            if (repeatCustomWeek == null || repeatCustomWeek.isEmpty()) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
        } else if (repeatType != REPEAT_TYPE_NEVER) {
            if (isNullOrEmpty(repeatEndTime)) {
                return CommonResult.failed(CommonCodeEnum.INVALID_PARAM);
            }
        }

        //拼接查询条件
        LambdaQueryWrapper<SmsMeetingRoomInfo> meetingRoomQueryWrapper = new LambdaQueryWrapper<>();
        meetingRoomQueryWrapper.eq(SmsMeetingRoomInfo::getIsUse, true);
        if (deviceList != null && !deviceList.isEmpty()) {
            for (String device : deviceList) {
                meetingRoomQueryWrapper.like(SmsMeetingRoomInfo::getDeviceList, device);
            }
        }
        if (!isNullOrEmpty(capacity)) {
            meetingRoomQueryWrapper.ge(SmsMeetingRoomInfo::getCapacity, capacity);
        }
        if (!isNullOrEmpty(searchStr)) {
            meetingRoomQueryWrapper.like(SmsMeetingRoomInfo::getName, searchStr)
                    .or().like(SmsMeetingRoomInfo::getRoomNum, searchStr)
                    .or().like(SmsMeetingRoomInfo::getRemark, searchStr);
        }
        List<SmsMeetingRoomInfo> meetingRoomList = smsMeetingRoomInfoMapper.selectList(meetingRoomQueryWrapper);
        if (meetingRoomList.isEmpty()) {
            return CommonResult.failed(CommonCodeEnum.NO_MEETING_ROOM_MEET_THE_CONDITION);
        }

        Iterator<SmsMeetingRoomInfo> iterator = meetingRoomList.iterator();
        while (iterator.hasNext()) {
            SmsMeetingRoomInfo meetingRoom = iterator.next();
            LambdaQueryWrapper<SmsMeetingRoomPredetermineApply> applyQueryWrapper = new LambdaQueryWrapper<>();
            applyQueryWrapper.eq(SmsMeetingRoomPredetermineApply::getMeetingRoomId, meetingRoom.getId())
                    .eq(SmsMeetingRoomPredetermineApply::getAllowStatus, MEETING_ALLOW_STATUS_PASS);

            //获取指定日期集合
            if (repeatType == REPEAT_TYPE_NEVER) {
                applyQueryWrapper.not(wrapper -> wrapper.le(SmsMeetingRoomPredetermineApply::getEndTime, startDate)
                        .or().ge(SmsMeetingRoomPredetermineApply::getStartTime, endDate));
            } else if (repeatType == REPEAT_TYPE_EVERY_DAY || repeatType == REPEAT_TYPE_EVERY_WEEK) {
                List<Date> dateList = new ArrayList<>();
                Date date = TimeUtil.StringtoDate(TimeUtil.Dateformat(startDate));
                while (date.compareTo(repeatEndTime) <= 0) {
                    dateList.add(date);
                    date = TimeUtil.AddDays(date, repeatType == REPEAT_TYPE_EVERY_DAY ? 1 : 7);
                }

                for (Date appointDate : dateList) {
                    applyQueryWrapper.not(wrapper -> wrapper.le(SmsMeetingRoomPredetermineApply::getEndTime, TimeUtil.GetSameDayDate(startDate, appointDate))
                            .or().ge(SmsMeetingRoomPredetermineApply::getStartTime, TimeUtil.GetSameDayDate(endDate, appointDate)));
                }
            } else if (repeatType == REPEAT_TYPE_EVERY_MONTH) {
                List<Date> dateList = new ArrayList<>();
                Date date = TimeUtil.StringtoDate(TimeUtil.Dateformat(startDate));
                if (!TimeUtil.isLastDayOfMonth(date)) {
                    while (date.compareTo(repeatEndTime) <= 0) {
                        dateList.add(date);
                        date = TimeUtil.AddMonths(date, 1);
                    }
                } else {
                    while (date.compareTo(repeatEndTime) <= 0) {
                        dateList.add(date);
                        date = TimeUtil.GetEndDateOfMonth(TimeUtil.AddDays(date, 1));
                    }
                }

                for (Date appointDate : dateList) {
                    applyQueryWrapper.not(wrapper -> wrapper.le(SmsMeetingRoomPredetermineApply::getEndTime, TimeUtil.GetSameDayDate(startDate, appointDate))
                            .or().ge(SmsMeetingRoomPredetermineApply::getStartTime, TimeUtil.GetSameDayDate(endDate, appointDate)));
                }
            } else if (repeatType == REPEAT_TYPE_EVERY_CUSTOM) {
                List<Date> dateList = new ArrayList<>();
                for (Integer week : repeatCustomWeek) {
                    Calendar weekBgCal = Calendar.getInstance();
                    weekBgCal.setTime(startDate);
                    weekBgCal.set(Calendar.DAY_OF_WEEK, week);
                    Date date = weekBgCal.getTime();
                    while (date.compareTo(repeatEndTime) <= 0) {
                        dateList.add(date);
                        date = TimeUtil.AddDays(date, 7);
                    }

                    for (Date appointDate : dateList) {
                        applyQueryWrapper.not(wrapper -> wrapper.le(SmsMeetingRoomPredetermineApply::getEndTime, TimeUtil.GetSameDayDate(startDate, appointDate))
                                .or().ge(SmsMeetingRoomPredetermineApply::getStartTime, TimeUtil.GetSameDayDate(endDate, appointDate)));
                    }
                }

            }

            //若有时间冲突的申请,则移除该会议室
            Integer count = smsMeetingRoomPredetermineApplyMapper.selectCount(applyQueryWrapper);
            if (count > 0) {
                iterator.remove();
            }
        }

        //包装数据 组织-楼栋-楼层-会议室
        ArrayList<OrgMeetingBuildingResp> meetingBuildingTree = new ArrayList<>();
        //获取组织
        LambdaQueryWrapper<SmsOrgStructure> orgQueryWrapper = new LambdaQueryWrapper<SmsOrgStructure>().eq(SmsOrgStructure::getLevel, 1);
        List<SmsOrgStructure> orgList = smsOrgStructureMapper.selectList(orgQueryWrapper);
        int count = 0;
        for (SmsOrgStructure org : orgList) {
            OrgMeetingBuildingResp orgMeetingBuildingResp = new OrgMeetingBuildingResp();
            orgMeetingBuildingResp.setOrgId(String.valueOf(org.getId()));
            orgMeetingBuildingResp.setOrgName(org.getOrgName());
            //获取楼栋
            ArrayList<MeetingBuildingResp> buildingRespList = new ArrayList<>();
            LambdaQueryWrapper<SmsMeetingBuilding> buildingQueryWrapper = new LambdaQueryWrapper<SmsMeetingBuilding>()
                    .eq(SmsMeetingBuilding::getOrgId, org.getId());
            List<SmsMeetingBuilding> buildingList = smsMeetingBuildingMapper.selectList(buildingQueryWrapper);
            for (SmsMeetingBuilding building : buildingList) {
                MeetingBuildingResp buildingResp = new MeetingBuildingResp();
                buildingResp.setId(building.getId());
                buildingResp.setName(building.getName());
                //获取楼层
                ArrayList<MeetingBuildingFloorResp> floorRespList = new ArrayList<>();
                LambdaQueryWrapper<SmsMeetingBuildingFloor> floorQueryWrapper = new LambdaQueryWrapper<SmsMeetingBuildingFloor>()
                        .eq(SmsMeetingBuildingFloor::getBuildingId, building.getId());
                List<SmsMeetingBuildingFloor> floorList = smsMeetingBuildingFloorMapper.selectList(floorQueryWrapper);
                for (SmsMeetingBuildingFloor floor : floorList) {
                    MeetingBuildingFloorResp floorResp = new MeetingBuildingFloorResp();
                    floorResp.setFloorId(floor.getId());
                    floorResp.setFloorName(floor.getName());

                    //获取会议室
                    List<SmsMeetingRoomInfo> meetingRoomInfoList = meetingRoomList.stream().filter(meetingRoom ->
                            Objects.equals(meetingRoom.getFloorId(), floor.getId())).collect(Collectors.toList());
                    List<SmsMeetingRoomInfoResp> meetingRoomRespList = new ArrayList<>();
                    for (SmsMeetingRoomInfo meetingRoom : meetingRoomInfoList) {
                        count++;
                        SmsMeetingRoomInfoResp meetingRoomResp = new SmsMeetingRoomInfoResp();
                        BeanUtils.copyProperties(meetingRoom, meetingRoomResp);
                        meetingRoomResp.setDeviceList(JSON.parseArray(meetingRoom.getDeviceList(), String.class));
                        meetingRoomResp.setStartTimeLimit(TimeUtil.TimeFormatSting(meetingRoom.getStartTimeLimit()));
                        meetingRoomResp.setEndTimeLimit(TimeUtil.TimeFormatSting(meetingRoom.getEndTimeLimit()));
                        List<String> allowUserIdList = JSON.parseArray(meetingRoom.getAllowUserIdList(), String.class);
                        if (allowUserIdList != null && !allowUserIdList.isEmpty()) {
                            meetingRoomResp.setAllowUserIdList(allowUserIdList);
                            List<SmsSearchUserResp> userRespArrayList = new ArrayList<>();
                            for (String allowUserId : allowUserIdList) {
                                //获取当前用户信息并进行设置
                                SmsUser smsUser = cacheUtil.getUserInfo(allowUserId);
                                SmsSearchUserResp userResp = new SmsSearchUserResp();
                                if (smsUser != null) {
                                    BeanUtils.copyProperties(smsUser, userResp);
                                }
                                userRespArrayList.add(userResp);
                            }
                            meetingRoomResp.setAllowUserInfo(userRespArrayList);
                        }
                        meetingRoomResp.setOrgName(org.getOrgName());
                        meetingRoomResp.setBuildingName(building.getName());
                        meetingRoomResp.setFloorName(floor.getName());

                        meetingRoomRespList.add(meetingRoomResp);
                    }

                    floorResp.setMeetingRoomList(meetingRoomRespList);
                    floorRespList.add(floorResp);
                }

                buildingResp.setFloorRespList(floorRespList);
                buildingRespList.add(buildingResp);
            }

            orgMeetingBuildingResp.setBuildingRespList(buildingRespList);
            meetingBuildingTree.add(orgMeetingBuildingResp);
        }

        return CommonResult.success(meetingBuildingTree, count);
    }

}
